Built-in
parts of jQuery can be extended as well. Rather than adding new
methods, we can customize existing ones. A common desire, for example,
is to expand on the selector expressions provided by jQuery to provide more esoteric options.
The easiest type of selector expression to add is a pseudo-class; these are the expressions that start with a colon, such as :checked or :nth-child(). To illustrate the process of creating a selector expression, we'll build a pseudo-class called :css(). This new selector will allow us to locate elements based on the numeric values of their CSS attributes.
When using a selector expression to find elements, jQuery looks for instructions in an internal map called expr.
This map contains JavaScript code to execute on an element, causing the
element to be contained in the result set if the code evaluates to true. We can add new expressions to this map using the $.extend() function.
jQuery.extend(jQuery.expr[':'], {
'css': function(element, index, matches, set) {
var parts = /([\w-]+)\s*([<>=]+)\s*(\d+)/
.exec(matches[3]);
var value = parseFloat(jQuery(element).css(parts[1]));
switch (parts[2]) {
case '<':
return value < parseInt(parts[3]);
case '<=':
return value <= parseInt(parts[3]);
case '=':
case '==':
return value == parseInt(parts[3]);
case '>=':
return value >= parseInt(parts[3]);
case '>':
return value > parseInt(parts[3]);
}
}
});
This code tells jQuery that css
is a valid string that can follow a colon in a selector expression, and
that when it is encountered, the given function should be called to
determine whether the element should be included in the result set.
The function that is evaluated here is passed four parameters:
element: The DOM element under consideration. This is needed for most selectors.
index: The index of the DOM element within the result set. This is helpful for selectors like :eq() and :lt().
matches: An array containing the result of the regular expression that was used to parse this selector. Typically, matches[3] is the only relevant item in the array; in a selector of the form :a(b), the matches[3] item contains b, the text within the parentheses.
set: The entire set of DOM elements matched up to this point. This parameter is rarely needed.
Pseudo-class selectors
need to use the information contained in these four arguments to
determine whether or not the element belongs in the result set. In this
case, and matches are all that we require. element
In our selector function, we first break down the selector into usable parts with a regular expression. We want a selector like :css(width < 200) to return all elements with a width of less than 200. So we need to look at the text within the parentheses to pull out the property name (width), comparison operator (<), and value to compare against (200). The regular expression /([\w-]+)\s*([<>=]+)\s*(\d+)/ performs this search, placing these three portions of the string into the parts array for our use.
Next, we need to fetch the current value of the property. We can use jQuery's .css()
method to return the value of the property that has been named in the
selector. Since this property is returned as a string, we use parseFloat() to turn it into a number.
Finally, we perform the actual comparison. A switch
statement determines which type of comparison is done depending on the
content of the selector, and the result of the comparison (true or false) is returned.
We now have a new selector expression we can use anywhere in our jQuery code. A simple HTML document can demonstrate this:
<body>
<div>Deserunt mollit anim id est laborum</div>
<div>Ullamco</div>
<div>Ut enim ad minim veniam laboris</div>
<div>Quis nostrud exercitation consequat nisi</div>
<div>Ut aliquip</div>
<div>Commodo</div>
<div>Lorem ipsum dolor sit amet ex ea</div>
</body>
With our new selector, it becomes trivial to highlight the smaller items in this list:
$(document).ready(function() {
$('div:css(width < 100)').addClass('highlight');
});